package com.jrfid.smartcabinet.demo.ui.login.face

import android.graphics.Point
import android.hardware.Camera
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.view.Surface
import android.view.SurfaceView
import androidx.annotation.Nullable
import androidx.appcompat.app.AlertDialog
import androidx.navigation.fragment.findNavController
import com.arcsoft.face.*
import com.arcsoft.face.enums.DetectFaceOrientPriority
import com.arcsoft.face.enums.DetectMode
import com.blankj.utilcode.util.LogUtils
import com.blankj.utilcode.util.ToastUtils
import com.jrfid.smartcabinet.demo.R
import com.jrfid.smartcabinet.demo.arcface.*
import com.jrfid.smartcabinet.demo.arcface.camera.CameraHelper
import com.jrfid.smartcabinet.demo.arcface.camera.CameraListener
import com.jrfid.smartcabinet.demo.arcface.face.*
import com.jrfid.smartcabinet.demo.arcface.model.DrawInfo
import com.jrfid.smartcabinet.demo.arcface.model.FacePreviewInfo
import com.jrfid.smartcabinet.demo.base.BaseFragment
import com.jrfid.smartcabinet.demo.databinding.LoginByFaceFragmentBinding
import com.jrfid.smartcabinet.demo.model.DrawFaceInfoModel
import com.jrfid.smartcabinet.demo.utils.FaceUtils
import com.jrfid.smartcabinet.demo.view.FaceRectView
import io.reactivex.Observable
import io.reactivex.ObservableOnSubscribe
import io.reactivex.Observer
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import java.lang.Exception
import java.lang.String
import java.util.ArrayList
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit

class LoginByFaceFragment : BaseFragment<LoginByFaceFragmentBinding, LoginByFaceViewModel>(LoginByFaceViewModel::class.java) {

    companion object {
        fun newInstance() = LoginByFaceFragment()
    }

    override fun getLayoutId(): Int = R.layout.login_by_face_fragment

    private var isNeedActive = false
    private lateinit var previewView: SurfaceView
    private lateinit var faceRectView: FaceRectView

    /**
     * 当前识别到的人脸
     */
    private var currentFaceInfoModel: DrawFaceInfoModel? = DrawFaceInfoModel()
    private var threadPoolExecutor = Executors.newFixedThreadPool(2)
    private val tagGetFaceLiveness = 100
    private val tagGetFaceFeature = 101
    private val tagFaceRegistered = 102
    private val tagLogin = 103

    private val handler = object : Handler(Looper.getMainLooper()) {
        override fun handleMessage(msg: Message) {
            super.handleMessage(msg)
            when (msg.what) {
                tagGetFaceFeature -> {
                    val faceFeature = msg.obj as FaceFeature
                    val faceId = msg.arg1
                    if (faceId == currentFaceInfoModel?.faceId) {
                        currentFaceInfoModel?.let {
                            it.faceFeature = faceFeature
                            faceRectView.setDrawFaceInfo(currentFaceInfoModel)
                        }
                    }
                }
                tagGetFaceLiveness -> {
                    val livenessInfo = msg.obj as Int
                    val faceId = msg.arg1
                    if (faceId == currentFaceInfoModel?.faceId) {
                        currentFaceInfoModel?.let {
                            it.liveness = livenessInfo
                            faceRectView.setDrawFaceInfo(currentFaceInfoModel)
                        }
                    }
                }
                tagFaceRegistered -> {
                    val result = msg.obj as Boolean
                    val faceId = msg.arg1
                    if (faceId == currentFaceInfoModel?.faceId) {
                        currentFaceInfoModel?.let {
                            it.isRegistered = result
                            faceRectView.setDrawFaceInfo(currentFaceInfoModel)
                            if (result) {
                                sendEmptyMessageDelayed(tagLogin, 1000)
                            }
                        }
                    }
                }
                tagLogin -> {
                    ToastUtils.showShort(getString(R.string.text_login_successfully))
                    findNavController().navigate(R.id.action_loginFragment_to_homeFragment)
                }
            }
        }
    }

    override fun setData() {
        previewView = getDataBinding()!!.previewView
        faceRectView = getDataBinding()!!.faceView
        val activeFileInfo = ActiveFileInfo()
        val code = FaceEngine.getActiveFileInfo(requireContext(), activeFileInfo)
        if (code == ErrorInfo.MOK) {
            //正常获取人脸识别激活信息，初始化相机
            isNeedActive = false
            initFace()
        } else if (code == ErrorInfo.MERR_ASF_NOT_ACTIVATED || code == ErrorInfo.MERR_ASF_ACTIVE_FILE_NO_EXIST) {
            //激活人脸识别
            isNeedActive = true
        } else {
            isNeedActive = false
            ToastUtils.showLong(getStringFormat(R.string.text_face_init_error, code))
        }
    }

    /**
     * VIDEO模式人脸检测引擎，用于预览帧人脸追踪
     */
    private var ftEngine: FaceEngine? = null

    /**
     * 用于特征提取的引擎
     */
    private var frEngine: FaceEngine? = null

    /**
     * IMAGE模式活体检测引擎，用于预览帧人脸活体检测
     */
    private var flEngine: FaceEngine? = null

    private fun initFace() {
        ftEngine = FaceEngine()
        var code = ftEngine?.init(requireContext(), DetectMode.ASF_DETECT_MODE_VIDEO, DetectFaceOrientPriority.ASF_OP_ALL_OUT, 16, 10, FaceEngine.ASF_FACE_DETECT)
        if (code != ErrorInfo.MOK) {
            ftEngine = null
            LogUtils.dTag(TAG, "人脸识别引擎初始化失败")
        }
        frEngine = FaceEngine()
        code = frEngine?.init(requireContext(), DetectMode.ASF_DETECT_MODE_IMAGE, DetectFaceOrientPriority.ASF_OP_ALL_OUT, 16, 10, FaceEngine.ASF_FACE_RECOGNITION)
        if (code != ErrorInfo.MOK) {
            LogUtils.dTag(TAG, "frEngine 初始化错误，${code}")
        }
        flEngine = FaceEngine()
        code = flEngine?.init(requireContext(), DetectMode.ASF_DETECT_MODE_IMAGE, DetectFaceOrientPriority.ASF_OP_ALL_OUT, 16, 10, FaceEngine.ASF_LIVENESS)
        if (code != ErrorInfo.MOK) {
            LogUtils.dTag(TAG, "flEngine 初始化错误，${code}")
        }
    }

    private fun destroyEngine() {
        ftEngine?.let {
            synchronized(it) {
                val code = it.unInit()
                LogUtils.dTag(TAG, "ftEngine 回收，${code}")
            }
        }
        frEngine?.let {
            synchronized(it) {
                val code = it.unInit()
                LogUtils.dTag(TAG, "frEngine 回收，${code}")
            }
        }
        flEngine?.let {
            synchronized(it) {
                val code = it.unInit()
                LogUtils.dTag(TAG, "flEngine 回收，${code}")
            }
        }
    }

    private var cameraHelper: CameraHelper? = null
    private var drawHelper: DrawHelper? = null
    private var faceHelper: FaceHelper? = null
    private var previewSize: Camera.Size? = null

    private fun initCamera() {
        val cameraListener = object : CameraListener {
            override fun onCameraOpened(camera: Camera, cameraId: Int, displayOrientation: Int, isMirror: Boolean) {
                previewSize = camera.parameters.previewSize
                previewView.post {
                    drawHelper = DrawHelper(
                        previewSize!!.width,
                        previewSize!!.height,
                        previewView.width,
                        previewView.height,
                        displayOrientation,
                        cameraId,
                        isMirror,
                        false,
                        false
                    )
                }
            }

            override fun onPreview(nv21: ByteArray?, camera: Camera) {
                if (drawHelper == null) {
                    //等待初始化
                    return
                }
                if (nv21 == null) {
                    return
                }
                val data = nv21
                faceRectView.clearFaceInfo()
                val faceInfoList = arrayListOf<FaceInfo>()
                val code = ftEngine?.detectFaces(data, previewSize!!.width, previewSize!!.height, FaceEngine.CP_PAF_NV21, faceInfoList)
                if (code == ErrorInfo.MOK) {
                    if (faceInfoList.isEmpty()) {
                        LogUtils.dTag(TAG, "未识别到人脸")
                    } else {
                        //保留其中一个最大的人脸
                        TrackUtil.keepMaxFace(faceInfoList)
                        //绘制人脸框
                        val faceInfo = faceInfoList[0]
                        if (currentFaceInfoModel?.faceId == faceInfo.faceId) {
                            //没有换人，只重新绘制位置
                            currentFaceInfoModel?.let {
                                it.faceRect = drawHelper?.adjustRect(faceInfo.rect)
                                it.faceInfo = faceInfo
                                if (it.faceFeature == null) {
                                    threadPoolExecutor.execute(FaceRecognizeRunnable(data, faceInfo, previewSize!!.width, previewSize!!.height))
                                }
                                if (it.liveness != LivenessInfo.ALIVE) {
                                    threadPoolExecutor.execute(FaceLivenessDetectRunnable(data, faceInfo, previewSize!!.width, previewSize!!.height))
                                }
                            }
                        } else {
                            //换人过后，重新绘制
                            currentFaceInfoModel = DrawFaceInfoModel().apply {
                                this.faceInfo = faceInfoList[0]
                                this.faceRect = drawHelper?.adjustRect(faceInfo.rect)
                                this.faceId = faceInfoList[0].faceId
                            }
                            threadPoolExecutor.shutdownNow()
                            threadPoolExecutor = Executors.newFixedThreadPool(2)
                            //先检查活体信息与特征值
                            threadPoolExecutor.execute(FaceRecognizeRunnable(data, faceInfo, previewSize!!.width, previewSize!!.height))
                            threadPoolExecutor.execute(FaceLivenessDetectRunnable(data, faceInfo, previewSize!!.width, previewSize!!.height))
                        }
                        faceRectView.setDrawFaceInfo(currentFaceInfoModel)
                    }
                } else {
                    LogUtils.dTag(TAG, "人脸识别错误：${code}")
                }
            }

            override fun onCameraClosed() {

            }

            override fun onCameraError(e: Exception?) {

            }

            override fun onCameraConfigurationChanged(cameraID: Int, displayOrientation: Int) {
                drawHelper?.cameraDisplayOrientation = displayOrientation
            }
        }
        cameraHelper = CameraHelper.Builder()
            .previewViewSize(Point(previewView.measuredWidth, previewView.measuredHeight))
            //.rotation(requireActivity().windowManager.defaultDisplay.rotation)
            .rotation(Surface.ROTATION_0)
            .specificCameraId(Camera.CameraInfo.CAMERA_FACING_FRONT)
            .isMirror(false)
            .previewOn(previewView)
            .cameraListener(cameraListener)
            .build()
        cameraHelper?.init()
        cameraHelper?.start()
    }

    private fun destroyCamera() {
        cameraHelper?.release()
        cameraHelper = null
    }

    override fun onResume() {
        super.onResume()
        if (threadPoolExecutor.isShutdown) {
            threadPoolExecutor = Executors.newFixedThreadPool(2)
        }
        if (isNeedActive) {
            isNeedActive = false
            AlertDialog.Builder(requireContext())
                .setTitle(R.string.text_dialog_active_title)
                .setMessage(R.string.text_dialog_active_content)
                .setCancelable(false)
                .setNeutralButton(R.string.btn_cancel) { dialog, which -> }
                .setPositiveButton(R.string.btn_active) { dialog, which ->
                    val code = FaceEngine.activeOnline(requireContext(), FaceUtils.APP_ID, FaceUtils.SDK_KEY)
                    if (code == ErrorInfo.MOK) {
                        //激活成功，初始化相机
                        ToastUtils.showLong(getString(R.string.text_face_active_successfully))
                        initFace()
                        initCamera()
                    } else {
                        ToastUtils.showLong(getStringFormat(R.string.text_face_active_failed, code))
                    }
                }
                .show()
        } else {
            initCamera()
        }
    }

    inner class FaceRecognizeRunnable(private var data: ByteArray, private var faceInfo: FaceInfo, private var width: Int, private var height: Int) : Runnable {

        override fun run() {
            frEngine?.let {
                synchronized(it) {
                    if (currentFaceInfoModel?.faceId == faceInfo.faceId && currentFaceInfoModel?.faceFeature == null) {
                        val faceFeature = FaceFeature()
                        val code = it.extractFaceFeature(data, width, height, FaceEngine.CP_PAF_NV21, faceInfo, faceFeature)
                        if (code == ErrorInfo.MOK) {
                            if (currentFaceInfoModel?.faceId == faceInfo.faceId) {
                                var message = handler.obtainMessage(tagGetFaceFeature)
                                message.obj = faceFeature
                                message.arg1 = faceInfo.faceId
                                handler.sendMessage(message)
                                if (currentFaceInfoModel?.liveness == LivenessInfo.ALIVE) {
                                    //活体检测是真人，开始搜索时候已经注册
                                    val model = FaceServer.getInstance().getTopOfFaceLib(faceFeature)
                                    if (model != null && model.similar >= 0.8f) {
                                        //判断为已注册
                                        LogUtils.dTag(TAG, "人脸已注册")
                                        message = handler.obtainMessage(tagFaceRegistered)
                                        message.obj = true
                                        message.arg1 = faceInfo.faceId
                                        handler.sendMessage(message)
                                    }
                                }
                            }
                        } else {
                            LogUtils.dTag(TAG, "人脸特征检测失败：${code}")
                        }
                    }
                }
            }
        }
    }

    inner class FaceLivenessDetectRunnable(private var data: ByteArray, private var faceInfo: FaceInfo, private var width: Int, private var height: Int) : Runnable {

        override fun run() {
            flEngine?.let {
                synchronized(it) {
                    if (currentFaceInfoModel?.faceId == faceInfo.faceId && currentFaceInfoModel?.liveness != LivenessInfo.ALIVE) {
                        var code = it.process(data, width, height, FaceEngine.CP_PAF_NV21, arrayListOf(faceInfo), FaceEngine.ASF_LIVENESS)
                        var result = LivenessInfo.UNKNOWN
                        if (code == ErrorInfo.MOK) {
                            val infoList = arrayListOf<LivenessInfo>()
                            code = it.getLiveness(infoList)
                            if (code == ErrorInfo.MOK && infoList.isNotEmpty() && currentFaceInfoModel?.faceId == faceInfo.faceId) {
                                if (infoList[0].liveness == LivenessInfo.ALIVE && currentFaceInfoModel?.faceFeature != null) {
                                    //活体检测是真人，开始搜索时候已经注册
                                    val model = FaceServer.getInstance().getTopOfFaceLib(currentFaceInfoModel!!.faceFeature)
                                    if (model != null && model.similar >= 0.8f) {
                                        //判断为已注册
                                        LogUtils.dTag(TAG, "人脸已注册")
                                        val message = handler.obtainMessage(tagFaceRegistered)
                                        message.obj = true
                                        message.arg1 = faceInfo.faceId
                                        handler.sendMessage(message)
                                    }
                                }
                                result = infoList[0].liveness
                            } else {
                                LogUtils.dTag(TAG, "人脸活体信息获取失败：${code}")
                                result = LivenessInfo.UNKNOWN
                            }
                        } else {
                            LogUtils.dTag(TAG, "人脸活体检测失败：${code}")
                            result = LivenessInfo.UNKNOWN
                        }
                        val message = handler.obtainMessage(tagGetFaceLiveness)
                        message.obj = result
                        message.arg1 = faceInfo.faceId
                        handler.sendMessage(message)
                    }
                }
            }
        }

    }

    override fun onPause() {
        super.onPause()
        destroyCamera()
        faceRectView.clearFaceInfo()
        threadPoolExecutor.shutdownNow()
    }

    override fun onDestroyView() {
        super.onDestroyView()
        destroyEngine()
        handler.removeMessages(tagLogin)
        handler.removeMessages(tagFaceRegistered)
        handler.removeMessages(tagGetFaceFeature)
        handler.removeMessages(tagGetFaceLiveness)
    }
}